package jyrc;



import java.util.*;





/**
 * A JyRCServer aim to communicate with a messaging service, like IRC.
 * It represent the brigde with the JyRC client.
 * Please notice a JyRC server can also be called a user.
 */



public interface JyRCServer {

	/**
	 * Gets new posts.
	 * @return new posts that were sent on this server since the last call.
	 */
	Queue<JyRCPost> checkout() throws JyRCServerException;

	/**
	 * Sends a message to the messaging service through this server.
	 * @param txt the string to send. Do nothing if null or empty ("").
	 */
	void post(String txt) throws JyRCServerException;

	String username();
}





/**
 * A basic implementation of JyRCServer that allow us to make tests.
 * All users are connected to the same messaging service; this is a stand-alone channel.
 */



class DummyJyRCServer implements JyRCServer {

	private static final DummyInstantMessagingService imsrv = new DummyInstantMessagingService();
	private static class DummyInstantMessagingService {
		private final Collection<DummyJyRCServer> users;
		protected String topic;
		protected DummyInstantMessagingService() {
			users = new LinkedHashSet<DummyJyRCServer>();
			topic =  "/* "+this.getClass().getName()+" */";
			new Thread() {
				public void run() {
					Collection<DummyJyRCServer> deadUsers;
					do { deadUsers = null;
						for(DummyJyRCServer user : users) if(!user.isStillAlive()) {
							if(deadUsers == null)
								deadUsers = new LinkedList<DummyJyRCServer>();
							deadUsers.add(user);
						}
						if(deadUsers != null) for(DummyJyRCServer u : deadUsers)
							unsuscribe(u);
						try { sleep(53*1000);
						} catch(InterruptedException e) { return; }
					} while(true);
				}
			}.start();
		}
		synchronized void suscribe(DummyJyRCServer user) {
			post("/join",user);
			users.add(user);
			user.notify("/#topic "+topic);
		}
		synchronized void unsuscribe(DummyJyRCServer user, boolean notify) {
			users.remove(user);
			if(notify)
				post("/part",user);
		}            void unsuscribe(DummyJyRCServer user)	{ unsuscribe(user,true); }
		private void post(JyRCPost post, boolean analyze) {
			DummyJyRCServer sender = null;
			if(post.getUser() != null && post.getUser() instanceof DummyJyRCServer)
				sender = (DummyJyRCServer) post.getUser();
			if(post.isACommand() && analyze) {
				String cmd=post.getCommandName(), args=post.getCommandArgs();
				if(post.isAnInternalCommand()) {
					if(sender == null) {
					} else if("names".equals(cmd)) {
						for(JyRCServer user : users) sender.notifyPosted(new JyRCPost("/#join",user));
					}
					return;
				}
				if("nick".equals(cmd) && sender != null) {
					post("/#part",sender);
					post( new JyRCPost("/#join",args), false );
				} else if("topic".equals(cmd)) {
					this.topic = args;
				} else if("quit".equals(cmd) && sender != null) {
					unsuscribe(sender);
					return;
				}
			}
			for(DummyJyRCServer user : users)
				user.notifyPosted(post);
		}       void post(JyRCPost post)			{ post(post,true); }
		private void post(String txt, JyRCServer user)		{ post( new JyRCPost(txt,user), false ); }
		private void post(String txt)				{ post(txt,null); }
	}



	String name;
	Date lastCheckoutDate;
	Queue<JyRCPost> queue;

	DummyJyRCServer() {
		name = "" + (char)(Math.random()*26+65) + (char)(Math.random()*26+97);
		lastCheckoutDate = new Date();
		imsrv.suscribe(this);
	}

	protected void notify(String notification) {
		notifyPosted( new JyRCPost(notification) );
	}
	protected void notifyPosted(JyRCPost post) {
		if(queue == null)
			queue = new LinkedList<JyRCPost>();
		queue.add(post);
	}
	protected boolean isStillAlive() { return System.currentTimeMillis()-lastCheckoutDate.getTime() < 2*60*1000; }

	public Queue<JyRCPost> checkout() throws JyRCServerException {
		lastCheckoutDate = new Date();
		if(queue == null)
			return new LinkedList<JyRCPost>();
		Queue<JyRCPost> q = queue;
		queue = null;
		return q;
	}
	public void post(String txt) throws JyRCServerException {
		if(txt == null || "".equals(txt))
			return;
		JyRCPost post = new JyRCPost(txt,this);
		imsrv.post(post);
		if("nick".equals(post.getCommandName()))
			this.name = post.getCommandArgs();
	}
	public String username() { return name; }
}
